package cn.rubintry.bug.lib.network.http.viewmodel

import androidx.activity.ComponentActivity
import androidx.lifecycle.viewModelScope
import cn.rubintry.bug.common.base.event.NetWorkFailedEvent
import cn.rubintry.bug.lib.network.http.NetWorkService
import cn.rubintry.bug.lib.network.http.dialog.LoadingDialog
import com.blankj.utilcode.util.ActivityUtils
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import org.greenrobot.eventbus.EventBus
import retrofit2.HttpException
import java.net.ConnectException
import java.net.HttpURLConnection
import kotlin.reflect.KFunction
import kotlin.reflect.full.callSuspend

@Suppress("UNCHECKED_CAST")
abstract class BaseViewModel() : ActivityViewModel() {


    private var requestJob: Job ?= null


    /**
     * 处理异常
     */
    fun handleException(e : Throwable){
        when(e.javaClass){
            ConnectException::class.java -> {
                //网络错误，连接超时
                EventBus.getDefault().post(NetWorkFailedEvent("连接超时"))
            }

            retrofit2.HttpException::class.java -> {
                if((e as HttpException).code() == HttpURLConnection.HTTP_CLIENT_TIMEOUT){
                    //网络错误，连接超时
                    EventBus.getDefault().post(NetWorkFailedEvent("连接超时"))
                }
            }
        }
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    override fun onActivityStart() {
        super.onActivityStart()
        requestJob?.start()
    }

    override fun onActivityStop() {
        super.onActivityStop()

    }

    override fun onActivityDestroy() {
        super.onActivityDestroy()
        requestJob?.cancel()
    }




    fun <A : NetWorkService, R : Any> request(api: A, function:  KFunction<R>, callback: (R?) -> Unit, vararg params: Any){
        request(api , function , true , callback , *params)
    }


    /**
     * A: api接口
     * R: 返回值类型
     * @param api
     * @param function api里的方法,需要反射调用[KFunction.callSuspend]
     * @param params api里的方法的参数
     */
    fun <A : NetWorkService, R : Any> request(api: A, function:  KFunction<R>, showLoading : Boolean, callback: (R?) -> Unit, vararg params: Any){
        var loadingDialog: LoadingDialog?= null
        if(showLoading){
            ActivityUtils.getTopActivity()?.let { mActivity ->
                loadingDialog = LoadingDialog(mActivity)
                loadingDialog?.setOnDismissListener {
                    requestJob?.cancel()
                }
                if(loadingDialog?.isShowing == false){
                    loadingDialog?.show()
                }
            }
        }
        requestJob = viewModelScope.launch {
            flow {
                try {
                    val resultValue = function.callSuspend(api , *params)
                    emit(resultValue)
                }catch (ex: Exception){
                    //此处仅处理反射异常
                    CoroutineScope(Dispatchers.Main).launch{
                        loadingDialog?.dismiss()
                    }
                    handleException(ex)
                    ex.printStackTrace()
                }
            }.flowOn(Dispatchers.IO)
                .catch { e ->
                    //此处处理请求异常
                    CoroutineScope(Dispatchers.Main).launch{
                        loadingDialog?.dismiss()
                    }
                    handleException(e)
                    e.printStackTrace()
                }.collect{
                    loadingDialog?.dismiss()
                    callback.invoke(it)
                }
        }

    }


}